import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;

/**
 * Klasse die de multicast aanvragen afwerkt.
 * 
 * @author Jelle Polders
 *
 */
public class Multicaster {
	protected MulticastSocket multicastSocket;
	protected InetAddress group;
	protected String ip;
	protected int port;
	protected int processID;
	protected VectorClock clock;
	
	/**
	 * Constructor
	 * 
	 * @param ip het ip-adres
	 * @param port de poort
	 * @param clock de klok
	 * @throws UnknownHostException geeft aan dat de host niet gevonden kan worden
	 */
	public Multicaster(String ip, int port, VectorClock clock) throws UnknownHostException {
		this.ip = ip;
		this.port = port;
		group = InetAddress.getByName(ip);
		multicastSocket = null;
		this.clock = clock;
	}
	
	/**
	 * Join een groep.
	 * 
	 * @throws IOException geeft aan dat er iets fout is gegaan bij de I/O
	 */
	public void join() throws IOException {
		multicastSocket = new MulticastSocket(port);
		multicastSocket.joinGroup(group);
	}
	
	/**
	 * Leave een groep.
	 * 
	 * @throws IOException geeft aan dat er iets fout is gegaan bij de I/O
	 */
	public void leave() throws IOException {
		multicastSocket.leaveGroup(group);
		multicastSocket.close();
		multicastSocket = null;
	}
	
	/**
	 * Zend een bericht.
	 * 
	 * @param msg het bericht dat verzonden moet worden
	 * @throws IOException geeft aan dat er iets fout is gegaan bij de I/O
	 * @return de vector clock horende bij het verzonden bericht
	 */
	public VectorClock send(Message msg) throws IOException {
		
		msg.setZenderID(processID);
		
		if (processID >= 0) {
			clock.increment();
			msg.setVectorClock(clock);
		}
		byte[] bytesMessage = msg.getBytes();
		
		DatagramPacket packet = new DatagramPacket(bytesMessage, bytesMessage.length, group, port);
		multicastSocket.send(packet);
		
		return clock; // nodig voor het locking algoritme van ricart & agrawala
	}
	
	/**
	 * Ontvangt berichten.
	 * 
	 * @return het bericht dat ontvangen is
	 * @throws Exception throwed een exceptie wanneer er nog geen multicast verbinding aangemaakt is.
	 */
	public Message receive() throws Exception {
		if (multicastSocket == null)
			throw new Exception("Er is nog geen multicast verbinding.");
		
		byte [] buffer = new byte[2048];

		DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
        multicastSocket.receive(packet);

        byte[] bytes = packet.getData();
                
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
		ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
		Message msg = (Message)objectInputStream.readObject();
		
		if (processID > -1 && msg.getZenderID() > -1 && msg.getZenderID() != processID) {
			clock.update(msg.getVectorClock());
		}
        
        return msg;
	}

	public int getProcessID() {
		return processID;
	}

	public void setProcessID(int processID) {
		this.processID = processID;
	}	
}
